#include "Precompile.h"
#include "ScriptManager.h"
#include "SceneManager.h"

namespace Core{
	ScriptManager::ScriptManager(EngineInterfacePtr engine):m_engine(engine){
	}

	ScriptManager::~ScriptManager(){
	}

	void ScriptManager::loadScriptFile(std::string &filename){
		Graphics::RenderContext *context = Graphics::GraphicsFactory::m_renderContext;

		std::string filePath = GeneralSettings::findFilePath(filename);
		if(filePath.empty()){
			LOG_ERROR("Script file not found.");
			assert(0);
			return;
		}
		
		std::map<std::string,std::pair<LuaPlus::LuaState*,std::string>>::iterator itr = m_states.find(filePath);
		if(itr != m_states.end()){
			LOG_ERROR("Script file already loaded.");
			assert(0);
			return;
		}

		LuaPlus::LuaState *state = LuaPlus::LuaState::Create(true);
		m_states[filePath] = std::pair<LuaPlus::LuaState*,std::string>(state,filename);
		
		initializeState(state);

		int result = state->DoFile(filePath.c_str());
		if(result != 0){
			LOG_ERROR("Unable to load lua file.");
			assert(0);
		}
	}

	void ScriptManager::unloadScriptFile(std::string &filename){
		std::string filePath = GeneralSettings::findFilePath(filename);
		std::map<std::string,std::pair<LuaPlus::LuaState*,std::string>>::iterator itr = m_states.find(filePath);
		if(itr == m_states.end()){
			LOG_ERROR("Script file not loaded.");
			assert(0);
			return;
		}

		Graphics::RenderContext *context = Graphics::GraphicsFactory::m_renderContext;
		context->m_renderSystem->unregisterLuaState(itr->second.first);

		LuaPlus::LuaState::Destroy(itr->second.first);
		m_states.erase(itr);
	}

	void ScriptManager::reloadLoadedScripts(){
		std::vector<std::string> filesToLoad;
		while(m_states.begin() != m_states.end()){
			filesToLoad.push_back(m_states.begin()->second.second);
			unloadScriptFile(std::string(m_states.begin()->second.second));
		}

		for(unsigned int i=0; i<filesToLoad.size(); i++){
			loadScriptFile(filesToLoad[i]);
		}
	}

	void ScriptManager::reloadSpecificScript(std::string &filename){
		unloadScriptFile(filename);
		loadScriptFile(filename);
	}

	void ScriptManager::initializeState(LuaPlus::LuaState* state){
		// Load key definitions
		int result = state->DoFile(GeneralSettings::findFilePath(std::string("KeyDefinitions.lua")).c_str());
		if(result != 0){
			LOG_ERROR("Unable to load key definition script.");
			assert(0);
		}

		// Create interface tables for usefull stuff
		LuaPlus::LuaObject globals = state->GetGlobals();

		// Engine object
		LuaPlus::LuaObject engineMetaTable = globals.CreateTable("EngineMetaTable");
		engineMetaTable.SetObject("__index",engineMetaTable);

		// Vector3 table
		{
			LuaPlus::LuaObject table = globals.CreateTable("Vector3MetaTable");
			table.SetObject("__index",table);
			table.Set("X",0);
			table.Set("Y",0);
			table.Set("Z",0);
		}

		// SceneManager table
		{
			LuaPlus::LuaObject table = globals.CreateTable("SceneManagerMetaTable");
			table.SetObject("__index",table);
			table.RegisterObjectDirect("AddMesh",(Graphics::SceneManager*)0,&Graphics::SceneManager::addMesh_luaWrap);
			table.RegisterObjectDirect("RemoveMesh",(Graphics::SceneManager*)0,&Graphics::SceneManager::removeMesh_luaWrap);
			table.RegisterObjectDirect("GetMesh",(Graphics::SceneManager*)0,&Graphics::SceneManager::getMesh_luaWrap);
		}

		// Mesh object table
		{
			LuaPlus::LuaObject table = globals.CreateTable("MeshMetaTable");
			table.SetObject("__index",table);
			table.RegisterObjectDirect("PlayAnimation",(Graphics::Mesh*)0,&Graphics::Mesh::playAnimation_luaWrap);
			table.RegisterObjectDirect("RotateX",(Graphics::Mesh*)0,&Graphics::Mesh::rotateX_luaWrap);
			table.RegisterObjectDirect("RotateY",(Graphics::Mesh*)0,&Graphics::Mesh::rotateY_luaWrap);
			table.RegisterObjectDirect("RotateZ",(Graphics::Mesh*)0,&Graphics::Mesh::rotateZ_luaWrap);
			table.RegisterObjectDirect("MoveX",(Graphics::Mesh*)0,&Graphics::Mesh::moveX_luaWrap);
			table.RegisterObjectDirect("MoveY",(Graphics::Mesh*)0,&Graphics::Mesh::moveY_luaWrap);
			table.RegisterObjectDirect("MoveZ",(Graphics::Mesh*)0,&Graphics::Mesh::moveZ_luaWrap);
			table.RegisterObjectDirect("MoveForward",(Graphics::Mesh*)0,&Graphics::Mesh::moveForward);
			table.RegisterObjectDirect("MoveUp",(Graphics::Mesh*)0,&Graphics::Mesh::moveUp);
			table.RegisterObjectDirect("MoveRight",(Graphics::Mesh*)0,&Graphics::Mesh::moveRight);
			table.RegisterObjectDirect("Scale",(Graphics::Mesh*)0,&Graphics::Mesh::scale);
			table.RegisterObjectDirect("GetPosition",(Graphics::Mesh*)0,&Graphics::Mesh::getPosition_luaWrap);
		}

		// Render system function object
		{
			// RenderManager table
			LuaPlus::LuaObject table = globals.CreateTable("RenderManagerMetaTable");
			table.SetObject("__index",table);
			table.RegisterObjectDirect("LoadModel",(Graphics::RenderSystem*)0,&Graphics::RenderSystem::loadModel_luaWrap);
			table.RegisterObjectDirect("GetSceneManager",(Graphics::RenderSystem*)0,&Graphics::RenderSystem::getSceneManager_luaWrap);
			table.RegisterObjectDirect("RegisterUpdateFunc",(Graphics::RenderSystem*)0,&Graphics::RenderSystem::registerRenderUpdateFunction_luaWrap);
			table.RegisterObjectDirect("UnregisterUpdateFunc",(Graphics::RenderSystem*)0,&Graphics::RenderSystem::unregisterRenderUpdateFunction_luaWrap);
			table.RegisterObjectDirect("SetSkyBox",(Graphics::RenderSystem*)0,&Graphics::RenderSystem::setSkyBox_luaWrap);

			LuaPlus::LuaObject object = state->BoxPointer(Graphics::GraphicsFactory::m_renderContext->m_renderSystem);
			object.SetMetaTable(table);
			engineMetaTable.SetObject("RenderManager",object);
		}

		// Input system function object
		{
			// InputManager table
			LuaPlus::LuaObject table = globals.CreateTable("InputManagerMetaTable");
			table.SetObject("__index",table);
			table.RegisterObjectDirect("KeyPressed",(Input::InputSystem*)0,&Input::InputSystem::keyPressed_luaWrap);

			LuaPlus::LuaObject object = state->BoxPointer(m_engine->getInputSystemPtr().get());
			object.SetMetaTable(table);
			engineMetaTable.SetObject("InputManager",object);
		}

		LuaPlus::LuaObject engineObject = state->BoxPointer(this);
		engineObject.SetMetaTable(engineMetaTable);
		globals.SetObject("Engine",engineObject);
	}
}